/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "imageknife_dispatcher.h"
#include "loader/imageknife_loader_internal.h"
#include "task_worker.h"
#include "zlib.h"
#include "interceptor.h"
#include "imageknife_task_internal.h"
#include "log.h"
#include <hitrace/trace.h>
namespace ImageKnifePro {
bool IsLoadInterceptor(Interceptor *interceptor)
{
    return dynamic_cast<LoadInterceptor *>(interceptor) != nullptr;
}


bool ExecuteResolveFunction(Interceptor *interceptor,
                            const std::shared_ptr<ImageKnifeTaskInternal>& task,
                            std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    std::string cacheInfo;
    if (task->cacheTask.type != CacheTaskType::NONE) {
        cacheInfo =  task->cacheTask.type == CacheTaskType::READ ? " Read" : " Write";
    }
    std::string &name = interceptor->name;
    IMAGE_KNIFE_LOG(LOG_DEBUG, "%{public}s %{public}s%{public}s Start",
                    task->GetTaskInfo().c_str(), name.c_str(), cacheInfo.c_str());
    OH_HiTrace_StartAsyncTrace((name + cacheInfo).c_str(), task->traceId);
    auto resolveResult = resolveCallback(task);
    if (task->IsDetached() && IsLoadInterceptor(interceptor)) {
        return true;
    }
    OH_HiTrace_FinishAsyncTrace((name + cacheInfo).c_str(), task->traceId);
    IMAGE_KNIFE_LOG(LOG_DEBUG, "%{public}s %{public}s%{public}s Finished",
                    task->GetTaskInfo().c_str(), name.c_str(), cacheInfo.c_str());

    return resolveResult;
}

bool Interceptor::Process(std::shared_ptr<ImageKnifeTask> task,
                          std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    auto taskInternal = std::dynamic_pointer_cast<ImageKnifeTaskInternal>(task);
    IMAGE_KNIFE_CHECK_RETURN(taskInternal == nullptr, false);
    auto request = taskInternal->GetImageKnifeRequestInternal();
    if (taskInternal->IsFatalErrorHappened() || request->IsDestroy()) {
        return false;
    }

    taskInternal->SetInterceptor(this);
    auto func = [this](std::shared_ptr<ImageKnifeTask> t) {return this->Resolve(t);};
    auto resolveFunction = (resolveCallback == nullptr) ? func : resolveCallback;

    bool result = ExecuteResolveFunction(this, taskInternal, resolveFunction);
    if (taskInternal->IsDetached() && IsLoadInterceptor(this)) {
        return true;
    }

    if (result) {
        taskInternal->ClearInterceptorPtr();
        return true;
    } else if (next_ != nullptr) {
        return next_->Process(task);
    } else {
        taskInternal->ClearInterceptorPtr();
        return false;
    }
}

void Interceptor::Cancel(std::shared_ptr<ImageKnifeTask> task)
{
}

void MemoryCacheInterceptor::SetNext(std::shared_ptr<MemoryCacheInterceptor> interceptor)
{
    next_ = interceptor;
}

void FileCacheInterceptor::SetNext(std::shared_ptr<FileCacheInterceptor> interceptor)
{
    next_ = interceptor;
}

bool IsImageCrc32Match(std::shared_ptr<ImageKnifeTaskInternal> taskInternal, std::shared_ptr<ImageKnifeOption> option)
{
    u_int32_t expectedCrc = option->crc32;
    std::vector<std::string> fallbackUrls = option->fallbackUrls;
    // 如果用户没有设置crc32的值，则直接返回
    if (expectedCrc == 0) {
        return true;
    }

    uLong crc = crc32(0L, Z_NULL, 0);
    auto data = taskInternal->product.imageBuffer;
    auto len = taskInternal->product.imageLength;
    if (data != nullptr && len > 0) {
        crc = crc32(crc, reinterpret_cast<const Bytef*>(data.get()), len);
    }
    if (expectedCrc == crc) {
        return true;
    }

    // clean up imageBuffer from remote, local pixmap do not need to clean.
    taskInternal->product.imageBuffer = nullptr;
    taskInternal->product.imageLength = 0;
    taskInternal->EchoError("CRC32 Check Failed");
    return false;
}

// 多域名自动重试
bool RetryFallbackUrls(std::shared_ptr<ImageKnifeTask> task, LoadInterceptor *downloadInterceptor)
{
    auto taskInternal = std::static_pointer_cast<ImageKnifeTaskInternal>(task);
    IMAGE_KNIFE_CHECK_RETURN(taskInternal == nullptr, false);
    IMAGE_KNIFE_CHECK_RETURN(downloadInterceptor == nullptr, false);

    auto request = taskInternal->GetImageKnifeRequestInternal();
    IMAGE_KNIFE_CHECK_RETURN(request == nullptr, false);
    auto option = request->GetImageKnifeOption();
    IMAGE_KNIFE_CHECK_RETURN(option == nullptr, false);

    // index初始为-1，即没有使用过备用地址，使用原地址下载
    for (int i = taskInternal->fallbackUrlIndex; i < (int)option->fallbackUrls.size(); i++) {
        if (request->IsDestroy() || taskInternal->IsFatalErrorHappened()) {
            return false;
        }
        // 首次下载使用loadSrc，失败后再尝试备用地址
        if (taskInternal->fallbackUrlIndex > -1) {
            taskInternal->SetFallbackUrl(option->fallbackUrls[i]);
            IMAGE_KNIFE_LOG(LOG_DEBUG, "%{public}s %{public}s Try Fallback Url %{public}d",
                            taskInternal->GetTaskInfo().c_str(), downloadInterceptor->name.c_str(), i);
        }
        // index增加需在Resolve之前，加载时可能会分离
        taskInternal->fallbackUrlIndex++;
        bool result = downloadInterceptor->Resolve(task);
        if (taskInternal->IsDetached()) {
            return true;
        }
        // 如果没有分离，进行CRC32Check
        if (result && IsImageCrc32Match(taskInternal, option)) {
            return true;
        }
    }

    // 为下一个拦截器重置imageSrc与fallbackUrl
    taskInternal->ResetFallbackUrl();
    return false;
}

void FinishLoadChain(std::shared_ptr<ImageKnifeTaskInternal> &task, const bool &result)
{
    auto request = task->GetImageKnifeRequestInternal();
    ImageKnifeLoaderInternal::RecordLoadingInfo(result, task->GetImageRequestType(), task->httpCode, request);
    if (request->IsSyncLoad()) {
        if (task->promiseSyncLoad != nullptr) {
            task->promiseSyncLoad->set_value(task);
        } else {
            task->EchoError("Promise SyncLoad Is Nullptr");
        }
    } else {
        TaskWorker::GetInstance()->PushTask([](std::shared_ptr<ImageKnifeTaskInternal> task) {
            ImageKnifeDispatcher::GetInstance().OnTaskComplete(task, IJobQueue::EndPhase::LOADING);
            }, nullptr, task, ImageKnifeQOS::USER_INITIATED);
    }
}

void LoadInterceptor::OnComplete(std::shared_ptr<ImageKnifeTask> task, bool result)
{
    auto taskInternal = std::dynamic_pointer_cast<ImageKnifeTaskInternal>(task);
    IMAGE_KNIFE_CHECK(taskInternal == nullptr);
    auto request = taskInternal->GetImageKnifeRequestInternal();
    IMAGE_KNIFE_CHECK(request == nullptr);
    auto option = request->GetImageKnifeOption();
    IMAGE_KNIFE_CHECK(option == nullptr);

    IMAGE_KNIFE_LOG(LOG_DEBUG, "%{public}s %{public}s Finished", taskInternal->GetTaskInfo().c_str(), name.c_str());
    OH_HiTrace_FinishAsyncTrace(name.c_str(), taskInternal->traceId);
    auto type = taskInternal->GetImageRequestType();
    taskInternal->ClearInterceptorPtr();

    // 取消或致命错误，直接结束
    if (request->IsDestroy() || taskInternal->IsFatalErrorHappened()) {
        FinishLoadChain(taskInternal, result);
        return;
    }

    // 下载成功进行Crc32校验
    if (result && type == ImageRequestType::MAIN_SRC && isLoadFromRemote) {
        result = IsImageCrc32Match(taskInternal, option);
    }

    if (!result) {
        // 失败进行重试
        if (isLoadFromRemote && type == ImageRequestType::MAIN_SRC &&
            taskInternal->fallbackUrlIndex < option->fallbackUrls.size()) {
            // task分离后不能标记为未分离状态，构造新的task
            taskInternal = std::make_shared<ImageKnifeTaskInternal>(taskInternal);
            request->InsertTask(taskInternal, type);
            result = RetryFallbackUrls(taskInternal, this);
            if (taskInternal->IsDetached()) {
                return;
            }
        }

        // 交由下一个拦截器
        if (!result && next_ != nullptr) {
            // 重试时新建的task如果没有分离，则可以复用
            if (taskInternal->IsDetached()) {
                taskInternal = std::make_shared<ImageKnifeTaskInternal>(taskInternal);
                request->InsertTask(taskInternal, type);
            }
            result = next_->Process(taskInternal);
            if (taskInternal->IsDetached()) {
                return;
            }
        }
    }

    // 加载结束
    if (result) {
        taskInternal->MarkSuccess();
    }
    FinishLoadChain(taskInternal, result);
}

void LoadInterceptor::Detach(std::shared_ptr<ImageKnifeTask> task)
{
    auto taskInternal = std::dynamic_pointer_cast<ImageKnifeTaskInternal>(task);
    IMAGE_KNIFE_CHECK(taskInternal == nullptr);
    taskInternal->Detach();
    IMAGE_KNIFE_LOG(LOG_DEBUG, "%{public}s %{public}s Detach", taskInternal->GetTaskInfo().c_str(), name.c_str());
}

bool LoadInterceptor::Process(std::shared_ptr<ImageKnifeTask> task,
                              std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    if (!isLoadFromRemote || task->GetImageRequestType() != ImageRequestType::MAIN_SRC) {
        return Interceptor::Process(task);
    }

    return Interceptor::Process(task, [this](std::shared_ptr<ImageKnifeTask> t) {
        return RetryFallbackUrls(t, this);
    });
}

void LoadInterceptor::SetNext(std::shared_ptr<LoadInterceptor> interceptor)
{
    next_ = interceptor;
}

void DecodeInterceptor::SetNext(std::shared_ptr<DecodeInterceptor> interceptor)
{
    next_ = interceptor;
}

bool DecodeInterceptor::Process(std::shared_ptr<ImageKnifeTask> task,
                                std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    return Interceptor::Process(task);
}

bool FileCacheInterceptor::Process(std::shared_ptr<ImageKnifeTask> task,
                                   std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    return Interceptor::Process(task);
}

bool MemoryCacheInterceptor::Process(std::shared_ptr<ImageKnifeTask> task,
                                     std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    return Interceptor::Process(task);
}
} // end of namespace
